﻿
namespace Mulala.Cavan.Core.Message
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using System.Text;
    using log4net;
    using Mulala.Cavan.Core.Extension;

    /// <summary>
    ///     application message class
    /// </summary>
    [Serializable]
    public class AppMessage
    {
        #region constructor
        /// <summary>
        ///     Initializes a new instance of the <see cref = "AppMessage" /> class.
        /// </summary>
        /// <param name = "reporter">Who did report this message? Instance of the ICoreController or IFacade</param>
        /// <param name = "severity">The severity.</param>
        /// <param name = "shortName">The short name.</param>
        /// <param name = "messageTemplate">Template to be used for 1) translating, 2) filling with template arguments. ("CannotAdd{0}{1}")</param>
        /// <param name = "arguments">The arguments.</param>
        public AppMessage(IMessagesReporter reporter, MessageSeverity severity, string shortName, string messageTemplate,
            params object[] arguments)
        {
            Contract.Requires(reporter.Is(), " reporter IMessagesReporter must be not null ");
            Contract.Requires(severity.Is(), " Cannot append message with severity == null ");
            Contract.Requires(!string.IsNullOrEmpty(messageTemplate), " Message Template cannot be null or empty ");
            Contract.Requires(!string.IsNullOrEmpty(shortName), " ShortName cannot be null or empty ");

            ReporterType = reporter.GetType();
            Severity = severity;
            ShortName = shortName;
            MessageTemplate = messageTemplate;
            Arguments = arguments;
        }
        #endregion constructor

        #region properties
        /// <summary>
        ///     Gets the severity.
        /// </summary>
        public virtual MessageSeverity Severity { get; private set; }

        /// <summary>
        ///     Gets or sets the short name.
        /// </summary>
        public virtual string ShortName { get; private set; }

        /// <summary>
        ///     Expects the .NET string template (e.g. "CannotAdd{0}{1}").
        ///     Positinal parameters are used from Argumetns array
        /// </summary>
        public virtual string MessageTemplate { get; private set; }

        /// <summary>
        ///     Gets the arguments.
        /// </summary>
        public virtual object[] Arguments { get; private set; }

        /// <summary>
        ///     Gets or sets the additional data.
        /// </summary>
        public virtual object AdditionalData { get; set; }

        /// <summary>
        ///     Who did report this message?
        /// </summary>
        public virtual Type ReporterType { get; private set; }

        /// <summary>
        ///     Logger based on Reporter (ReporterType.Log())
        /// </summary>
        public virtual ILog Logger
        {
            get { return ReporterType.Log(); }
        }
        #endregion properties

        #region ToDisplay() & ToString()
        /// <summary>
        ///     Returns the Message Template filled with Arguments[]
        /// </summary>
        /// <returns>Filled template</returns>
        public override string ToString()
        {
            return FillTemplate(MessageTemplate);
        }

        /// <summary>
        ///     Returns Localized MessageTemplate filled with Arguments[]
        /// </summary>
        /// <returns>Localized filled template</returns>
        public string ToDisplay()
        {
            return FillTemplate(MessageTemplate);
        }

        /// <summary>
        ///     Fills the provided template with Message arguments.
        ///     if exception even the StackTrace is added
        /// </summary>
        /// <param name = "template"></param>
        /// <returns></returns>
        string FillTemplate(string template)
        {
            var result = new StringBuilder();
            if (MessageTemplate.Is())
            {
                string message;
                try
                {
                    message = template.FormatWith(Arguments);
                }
                catch (FormatException fex)
                {
                    message = FillBrokenTemplate(template, Arguments);
                    Logger.Error(
                        "Provided message Template phrase(or its localized form) was not in correct format. Template:'"
                        + template + "' "
                        + " Expected was for example 'Template {0}' ", fex);
                }
                catch (Exception otherException) // messages must work anyway
                {
                    message = FillBrokenTemplate(template, Arguments);
                    Logger.Error("Provided message Template thrown an unexpected Exception Template:'"
                                 + template + "' "
                                 + " Expected was for example 'Template {0}'; not null params ", otherException);
                }
                result.Append(message);
                var ex = Arguments.Length > 0
                             ? Arguments[0] as Exception
                             : null;
                if (ex.Is())
                {
                    result.Append(" | " + ex.Source + " | " + ex.StackTrace);
                    if (ex.InnerException.Is())
                    {
                        result.Append(" INNER EXCEPTION: " + ex.InnerException.Message + " | "
                                      + ex.InnerException.StackTrace);
                    }
                }
            }
            return result.ToString();
        }

        /// <summary>
        ///     Provides filling the template for broken templates 
        ///     (incorrect format e.g. "Templat 0}" instead of "Template {0}", etc. }
        /// </summary>
        /// <param name = "template"></param>
        /// <param name = "arguments"></param>
        /// <returns></returns>
        static string FillBrokenTemplate(string template, params object[] arguments)
        {
            var result = new StringBuilder(template);
            if (arguments != null && arguments.Length > 0)
            {
                foreach (var argument in arguments)
                {
                    if (argument.Is())
                    {
                        result.Append(" '{" + argument + "}' ");
                    }
                }
            }

            return result.ToString();
        }
        #endregion
    }
}